package com.wn.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.text.SimpleDateFormat;

@Configuration
public class RedisConfig {
    @Bean
    public Jackson2JsonRedisSerializer jackson(){
        Jackson2JsonRedisSerializer jackson2=new Jackson2JsonRedisSerializer(Object.class);
        //序列化时添加对象信息：防止出现把Object转换成LinkedHashMap
        ObjectMapper om = new ObjectMapper();
         /*
        指定序列化的属性，
        	PropertyAccessor.ALL：表示属性
        	JsonAutoDetect.Visibility.ANY:表示包括private和public
        */
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
         /*
        指定序列化输入类型，也就是值类型
        */
        om.activateDefaultTyping(
                //只序列化非final的属性（例如不会把String,Integer等类型的数据按照我们的序列化方式）
                LaissezFaireSubTypeValidator.instance ,
                /*对于除了一些自然类型(String、Double、Integer、Double)类型外的非常量(non-final)类型，类型将会用在值的含义上。以便可以在JSON串中正确的推测出值所属的类型。*/
                ObjectMapper.DefaultTyping.NON_FINAL,
                //将多态信息作为数据的兄弟属性进行序列化
                JsonTypeInfo.As.PROPERTY);
        //对象属性为空时，不进行序列化储存
        om.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        //对日期格式化（按需设置）
        om.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        //把long类型的id以字符串形式格式化
        SimpleModule simpleModule = new SimpleModule();
        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
        om.registerModule(simpleModule);
        jackson2.setObjectMapper(om);
        return jackson2;
    }
    /**
     * redis配置类对象
     */
    @Bean
    public RedisTemplate<String,Object>  redisTemplate(RedisConnectionFactory redisConnectionFactory,
                                                       Jackson2JsonRedisSerializer jackson2){
        RedisTemplate<String,Object> rt=new RedisTemplate<>();
        rt.setConnectionFactory(redisConnectionFactory);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        /*
        设置string以及hash类型key的序列化
         */
        rt.setKeySerializer(stringRedisSerializer);
        rt.setHashKeySerializer(stringRedisSerializer);
        /*
        设置值的序列化
         */
        rt.setValueSerializer(jackson2);
        rt.setHashValueSerializer(jackson2);
        return rt;
    }
    /**
     * 指定使用redis进行缓存
     * @return
     */
    @Bean  //给容器注册一个Bean，返回缓存管理器,这里redisTemplate容器中有，所以会自动注入
    public CacheManager MyRedisCacheConfig(RedisConnectionFactory redisConnectionFactory, Jackson2JsonRedisSerializer<Object> sedisSerializer) {
        //1.创建RedisCacheWriter
        /**
         * 非锁方式：nonLockingRedisCacheWriter(RedisConnectionFactory connectionFactory);
         * 有锁方式：lockingRedisCacheWriter(RedisConnectionFactory connectionFactory);
         */
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);

        //3.传入 Jackson对象 并获取 RedisSerializationContext对象
        RedisSerializationContext<Object, Object> serializationContext = RedisSerializationContext.fromSerializer(sedisSerializer);
        //4.配置RedisCacheConfiguration
        /**
         * RedisCacheConfiguration.defaultCacheConfig()
         * 设置 value 的序列化 serializeValuesWit(SerializationPari<?> valueSerializationPari)
         * 设置 key 的序列化 serializeKeysWith(SerializationPari valueSerializationPari)
         */
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(serializationContext.getValueSerializationPair());
        return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);
    }
}